/*
 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved.
 *
 * This file is part of Harmattan SmashMiner OpenGL game application.
 * 
 * Harmattan SmashMiner OpenGL Example Application version 1.0.0
 *  
 * Latest update: 15.4.2011
 *
 * The Harmattan SmashMiner OpenGL example application demonstrates how to use 
 * the OpenGL ES in Harmattan devices.
 *
 * This example is provided as a starting point for 3rd party
 * developers (you) to ease the implementation of OpenGL based 
 * games. Ideas, parts of code or methodologies that this
 * example application uses can be freely used without restrictions.
 *
 * See file README.txt how to build and compile this example application
 * in the Harmattan SDK environment. 
 * 
 * See file INSTALL.txt to find out what is required to run this
 * application and how to install the application to the device or
 * alternatively in QEMU-emulator.
 *
 * The LICENSE.txt file explains the license this software is provided
 * with. A copy of the same license is included in this source file.
 * 
 */

/*
 * Copyright (C) 2011 by Nokia Corporation.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/*!
  \class GameAppViewer
  Extended QmlApplicationViewer containing \c GameSettings, \c GameWidget and
  \c MusicPlayer instance and takes care of starting the music playback \c GameAppViewer::startMusic().
  \c GameAppViewer extends \c QmlApplicationViewer class, which is automatically maintained by Qt Creator. That's how we will leave the \c QmlApplicationViewer class intact.
*/

#include "gameappviewer.h"

#include "highscoremodel.h"

#include <QTimer>
#include <QSize>
#include <QGraphicsObject>
#include <QDeclarativeContext>

#include <QResizeEvent>

#include <QApplication>
#include <QDesktopWidget>

#include <QDebug>

#include <QDBusConnection>

#include<QX11Info>
#include<X11/Xlib.h>


#include "gamesettings.h"

void setDontAnimate(QWidget *);


/// DBus connection. Needed for handling incoming phone calls.
static QDBusConnection dBusConn = QDBusConnection::systemBus();

/*!
  Constructs GameAppViewer
*/
GameAppViewer::GameAppViewer(QWidget *parent) 
    : QmlApplicationViewer(parent)
    // Init screen width and height.    
    , lastWidth(-1)
    , lastHeight(-1)
    , settings(new GameSettings(this))
    , gameWidget(new GameWidget(settings))
    , timer(NULL) // the timer object is created when the game mode is started.
{
    // initialize the random number generator.

    srand(time(NULL));

    QDeclarativeContext *context = rootContext();
    HighScoreModel *highScoreModel = settings->getHighScoreModel();
    context->setContextProperty("highScoreModel", highScoreModel);

    // Check that the widget is OK.
    // if not, then fatal error.
    if (!gameWidget)
        qFatal("gameWidget == NULL");

    // Init the music player
    musicPlayer = new MusicPlayer(settings->musicLevel(), this);
    connect(settings, SIGNAL(musicLevelChanged(int)), musicPlayer, SLOT(setVolumeLevel(int)));
    // the music is started delayed so that it doesn't
    // start before the QML UI is shown - see startMusic().

    // connect the signals for game mode pause/exit.
    QObject::connect(gameWidget, SIGNAL(gameModeExited()), this, SLOT(leaveGameMode()));
    QObject::connect(gameWidget, SIGNAL(pauseModeEntered()), this, SLOT(enterPauseMode()));
    QObject::connect(gameWidget, SIGNAL(pauseModeExited()), this, SLOT(leavePauseMode()));
    QObject::connect(gameWidget, SIGNAL(gameOver(int, FPtype)), this, SLOT(gameOverMode(int, FPtype)));

    // the opengl view needs to be set in a hidden state initially,
    // since the qml view is begin shown to the user. but do not set
    // gameWidget visible yet. it is done later, see initGameWidget().

    // Connect dbus message for the incoming phone call to slot callEvent().
    // Also activeCallAdded() signal send by QTelephonyCallList from Qt Mobility telephony API could be used.

    dBusConn.connect("com.nokia.csd.Call", "/com/nokia/csd/call",
                     "com.nokia.csd.Call", "Coming",
                     this, SLOT(callEvent(const QDBusMessage&)));
}

/*!
  Destructs GameAppViewer
*/
GameAppViewer::~GameAppViewer()
{
    musicPlayer->stop();

    delete gameWidget;
    gameWidget = NULL;

    if (timer != NULL) {
        delete timer;
        timer = NULL;
    }
}

/*!
  Initializes GameWidget
*/
GameWidget * GameAppViewer::initGameWidget()
{
#ifdef SMASHMINER_CONFIG_SET_GAMEVIEW_FULLSCREEN
    gameWidget->setWindowState(gameWidget->windowState() ^ Qt::WindowFullScreen);
#else   // SMASHMINER_CONFIG_SET_GAMEVIEW_FULLSCREEN
    gameWidget->setWindowState(gameWidget->windowState() ^ Qt::WindowMaximized);
#endif  // SMASHMINER_CONFIG_SET_GAMEVIEW_FULLSCREEN

    // first bring the gameWidget visible for a moment.
    // this is just to initialize it properly, so that
    // it appears more smoothly later when we actually
    // start the game.

    gameWidget->setVisible(true);

    // now hide it again...

    gameWidget->setVisible(false);

    // return gameWidget so that main() can add other
    // settings for it if needed.

    return gameWidget;
}

/*!
  Getter for GameSettings instance
*/
GameSettings * GameAppViewer::getSettings()
{
    return settings;
}

/*!
  Getter for SoundEngine instance
*/
SoundEngine * GameAppViewer::getSoundEngine()
{
    return gameWidget->getSoundEngine();
}

/*!
  Gets the height of the QML window title
*/
void GameAppViewer::getTitleHeight()
{
    QPoint p;

    // here we assume that the window is
    // either maximized or is a fullscreen one.
    // determine the upper margin compared to
    // a full-screen (desktop) window.

    p = mapToGlobal(QPoint(0, 0));
    const int titleHeight = p.y();

    QDeclarativeContext *context = rootContext();
    context->setContextProperty("titleHeight", titleHeight);
}

/// Start playing the game background music
void GameAppViewer::startMusic()
{
    musicPlayer->playOnLoop();
}

/*!
  Enters the game mode. From QML view to OpenGL view.
*/
void GameAppViewer::enterGameMode()
{
    // Disable screensaver when playing
    systemScreenSaver = new QtMobility::QSystemScreenSaver(this);
    systemScreenSaver->setScreenSaverInhibit();

    // initialize the game mode time measurement
    // and start the game loop timer.

    if (timer != NULL)
        qFatal("enterGameMode() : timer != NULL");

    gameWidget->initTimeMeasurement();

    timer = new QTimer(this);
    timer->setInterval(SMASHMINER_MAINLOOP_TIMER_MSECS);
    QObject::connect(timer, SIGNAL(timeout()), gameWidget, SLOT(updateGL()));

    gameWidget->setMainMenuActive(false);

    // check from gameWidget if it is in a pause mode.
    // if yes, then do not start the timer.

    if (!gameWidget->paused()) {
        // starting a new game
        gameWidget->initNewGame();
        timer->start();
    }

    setDontAnimate(gameWidget);

    // show the opengl view.
    gameWidget->setVisible(true);

    // TODO : currently when switching the windows,
    // the desktop icons momentarily appear and there
    // is an animation related to changing views.
    // todo : add some extra API calls here?

    XRaiseWindow(QX11Info::display(), gameWidget->effectiveWinId());



    // hide the qml view.
    setVisible(false);
}


/*!
  Leaves the game mode. From OpenGL view to QML view.
*/
void GameAppViewer::leaveGameMode()
{
    if (timer == NULL)
        qFatal("leaveGameMode() : timer == NULL");

    // stop (and delete) the game loop timer.
    timer->stop();
    delete timer;
    timer = NULL;

    //
    // disable animations for gameWidget and this object
    //

    setDontAnimate(gameWidget);
    setDontAnimate(this);

    // show the qml view.
    setVisible(true);

    //
    // raise the QML view
    //

    raise();

    // hide the opengl view.

    gameWidget->setVisible(false);

    // Enable screensaver when stopped playing.
    delete systemScreenSaver;
    systemScreenSaver = NULL;
}

/*!
  Enters the pause mode. Stays at OpenGL view.
*/
void GameAppViewer::enterPauseMode()
{
#ifdef SMASHMINER_CONFIG_ENABLE_DEBUG_CHECKS
    if (timer == NULL)
        qFatal("GameAppViewer::enterPauseMode() : timer == NULL");
#endif  // SMASHMINER_CONFIG_ENABLE_DEBUG_CHECKS

    timer->stop();

    // request one more update for the GameWidget graphics,
    // so that the pause mode text(ure)s will be shown.

    gameWidget->update();
}

/*!
  Leaves the pause mode. Enters the OpenGL view.
*/
void GameAppViewer::leavePauseMode()
{
#ifdef SMASHMINER_CONFIG_ENABLE_DEBUG_CHECKS
    if (timer == NULL)
        qFatal("GameAppViewer::leavePauseMode() : timer == NULL");
#endif  // SMASHMINER_CONFIG_ENABLE_DEBUG_CHECKS

    timer->start();
}

/*!
  Enters the game over mode. From OpenGL view to QML view.
*/
void GameAppViewer::gameOverMode(int score, FPtype gametime)
{
    if (settings->isHighScore(score)) {
        QString timeString;
        int minutes = (int) (gametime/60);
        int seconds = gametime -(minutes * 60);
        QTime time = QTime(0,minutes,seconds);
        timeString.append(time.toString("m:ss"));
        emit newHighScore(score, timeString);
        gameWidget->getSoundEngine()->play(SoundHighScore);
    } else {
        emit gameOver();
    }

    leaveGameMode();
}

/*!
  Slot for resize events from QML framework.
*/
void GameAppViewer::resizeEvent(QResizeEvent *event)
{
#if defined(Q_WS_MAEMO_5)

    // when going from game mode to pause mode,
    // an extra resize event (800x368) is received
    // before the final event (800x424).

    // -> filter the extra event away to avoid flicker.

    if (event->size().height() < 375) {
        qWarning() << "GameAppViewer::resizeEvent() : SKIPPED!";
        return;
    }

#endif  // defined(Q_WS_MAEMO_5)

    QDeclarativeView::resizeEvent(event);

    QSize size = this->size();
    const int width = size.width();
    const int height = size.height();

    if (width != lastWidth || height != lastHeight) {
        lastWidth = width;
        lastHeight = height;
        emit notifyResize(QVariant(width), QVariant(height));
    }
}

/*!
  Slot for incoming phone call events from DBus.
*/
void GameAppViewer::callEvent(const QDBusMessage& message)
{
    Q_UNUSED(message);
    if (gameWidget->isVisible())
        gameWidget->switchPauseMode();

}
